home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1996 March
/
EnigmA AMIGA RUN 05 (1996)(G.R. Edizioni)(IT)[!][issue 1996-03][Skylink CD IV].iso
/
earcd
/
comm2
/
parnet.lha
/
parnet
/
sources
/
par.asm
< prev
next >
Wrap
Assembly Source File
|
1993-11-18
|
20KB
|
736 lines
; PARALLEL PORT NETWORK LOW LEVEL ROUTINES
;
; CABLE: Connect D7-D0,SEL,POUT, and BUSY across,
; Connect ACK to SEL locally:
; Connect GND lines indicated
;
; (2-9) D7-D0 ------------ D7-D0
; (12) POUT ------------ POUT
; (11) BUSY ------------ BUSY PARALLEL PORT
; (13) SEL --+------+-- SEL
; (10) ACK -/ \- ACK
; (18-22) GND ------------ GND
;
; WARNING: you cannot connect RI on the serial port to your
; modem because it interferes with the parallel
; port's SEL line in this configuration.
;
; * 28K/sec bandwidth
; * # machines depends on extra hardware for buffering,
; but 3 ought to work fine without any extra hardware.
;
; (network protocol can now handle 254)
;
; Data Line Definitions:
; CIAA PORTB : D7-D0 used for byte data transfer
; CIAB PORTA : D2-D0 used for line aquisition and
; handshaking (SEL,POUT,BUSY)
;
; All lines pulled up. Thus, asserted state is a 0. Idle
; state is an undriven (1). Protocol transfers a byte at
; a time. Protocol is ethernet style with a small window
; of error in the line aquisition routine.
;
; Note: Timeouts should be set around a second. Ideally
; defaults should be fixed on faster machines.
;
; CIAB PORTA D0 ~ACK hand shake
; D1 ~REQ hand shake
; D2 CTL 1 = special byte, 0 = data byte.
; (for address mark, valid when ~REQ
; goes low. For EOP mark, sample
; when ~REQ goes high)
;
; PROTOCOL
;
; HandShake (Reader <- Writer transfer). A Handshake
; sequence transfers TWO bytes of information.
;
; WRITER READER
; |-> place data, ~REQ->0
; | wait for ~REQ->0
; | read data & store
; | set ~ACK->0
; | wait ~ACK->0
; | place data, ~REQ->1
; | wait for ~REQ->1
; | read data & store
; | set ~ACK->1
; | wait ~ACK->1
; |<- LOOP (2 bytes written) LOOP (2 bytes read)
;
;
; Read: (1) Determine if your machine is being addressed
; (~REQ=0, data=myaddress, CTL=1)
; (1) set DDR for ~ACK to output and
; (2) Handshake sequence for the address mark, only
; first byte valid.
; (3) Handshake sequence for data util rcv byte
; with CTL = 1 (EOP), byte must == 0.
; (4) Set ~ACK (bit 0) to input
;
; Write: (1) AQUIRE THE NETWORK (see below)
; involves gaining control and then setting the
; DDR for CTL and ~REQ to outputs.
;
; Also checks if somebody is writing to us,
; in which case -2 is returned instantaniously
; indicating we should do a ParRead().
;
; (2) Handshake sequence for address mark, send dest
; address (second byte garbage). Note that CTL->1
; *BEFORE* we set ~REQ->0
; (3) Handshake sequence for data bytes
; (4) Handshake sequence for EOP mark (Note that CTL->1
; *AFTER* we get the ~ACK->1 and before release
; ~REQ (->1). Only firstbyte valid and set to 0.
;
; (5) Set ~ACK and ~CTL to inputs
;
; AQUIRE: Line aquisition prevents two people from writing to
; the net at the same time.
;
; * A line is considered aquired if ANY of the 3
; control lines is 0.
; * If network is not aquired:
; - set ~ACK to output and 1
; - bclr ~ACK to 0 and bne success
; (else set ~ACK to input and try again)
; - set data lines to output and place my address
; (Must ]be done before CTL is glitched)
; - set ~CTL to output and 1
; - set CTL to 0 and then 1 (glitch it) to cause
; FLAG interrupt on all other machines
; - set ~REQ to output and 0 (beginning of handshake
; sequence)
; - lastly, release ~ACK by setting it to an input
;
; Note that at all times at least one line is a 0
; so no other machine will attempt to aquire the net.
;
; Note that the destination address is placed on the data
; lines after we have aquired the line but before we glitch
; the CTL line to cause an interrupt. This allows the
; other machines to instantaniously determine who is being
; addressed.
;
; Note that the CTL line gets glitched at the end of a packet
; too for the EOP mark. In this case there is a 0 on the
; data lines so while an interrupt is generated, nobody
; thinks they are being addressed.
INCLUDE "exec/types.i"
INCLUDE "exec/execbase.i"
XREF _intena
DISABLE MACRO
MOVE.W #$04000,_intena *(NOT IF_SETCLR)+IF_INTEN
ADDQ.B #1,IDNestCnt(A6)
ENDM
ENABLE MACRO
SUBQ.B #1,IDNestCnt(A6)
BGE.S ENABLE\@
MOVE.W #$0C000,_intena *IF_SETCLR+IF_INTEN
ENABLE\@
ENDM
section __MERGED,DATA
xdef _ParLLTimeout
xdef _ParDAT
xdef _ParDDR
xdef _ParCollision1
xdef _ParCollision2
xref _SysBase
; Note, the timeout is set manually on init by using the
; timer.device to time one second.
_ParLLTimeout dc.l 1000000 ; default timeout value (count). 1 second
_ParDAT dc.l $BFE101 ; data port
_ParDDR dc.l $BFE301 ; data ddr
_ParCAT dc.l $BFD000 ; ctl port (D0-D2)
_ParCDR dc.l $BFD200 ; ctl ddr (D0-D2)
_ParNetAddr dc.b 0 ; default network address
_DummyBuf dc.b 0 ; dummy buffer
dc.b 0 ; dummy buffer
dc.b 0 ; pad
_ParCollision1 dc.l 0 ; statistics
_ParCollision2 dc.l 0 ;
Null dc.l 0 ; always 0
dc.l 0 ; always 0
ifnd DEBUG
DEBUG set 0
endc
ifne DEBUG
xdef _ParDebug
_ParDebug ds.l 16 ; 16 longword debug entries
endc
section text,code
; (void) ParAddress(myaddr)
; Set my address to (1-254)
;
; 0 and 255 are reserved!
;
; int = ParDataReady()
; returns 1 if packet pending
; returns 0 if line is currently idle
; returns -1 if packet isn't for you
;
; if line has been aquired but no control address has been
; put on it yet, ParDataReady() will wait for a control
; address. Thus, after a signal, a single call to
; ParDataReady() should suffice.
;
; n = ParReadV (buf1, bytes1, buf2, bytes2, ..., NULL, NULL);
; (buffer sizes must be even)
; n = ParRead (buf, bytes)
; read a pending packet. Returns n = -1 (1 second timeout
; no packet pending), n = 0 to bytes -1 (1 second timeout
; after transmission interrupted), n = bytes (success),
; or n > bytes (transmitting machine's packet was larger
; than we can handle, extra bytes thrown out)
;
; NOTE: requesting an odd number of bytes is O.K. but
; if you request N where N is odd and the writer
; sends N + 1 you will never know (N will be
; returned). See ParWrite() below
;
; n = ParWriteV (destadr, buf1, bytes1, buf2, bytes2, ..., NULL, NULL);
; (buffer sizes must be even)
; n = ParWrite(destadr, buf, bytes)
; write a packet. Returns:
; n = -2 Cannot write anything, a packet is pending
; (instantanious)
; n = -1 Destination machine does not respond (1 sec to)
; n = N N bytes written ok (success if n == bytes)
;
; NOTE: sending an odd number of bytes is O.K. but if
; you write N where N is odd and the reader requests
; N + 1 he will get N + 1 the last byte being garbage.
;
;
; CIAA PORTB : DATA ONLY
; CIAB PORTA : D0 ~DAck
; D1 ~DRdy
; D2 Ctl
xdef _ParRead
xdef _ParReadV
xdef _ParWrite
xdef _ParWriteV
xdef @ParAddress
xdef _ParAddress
xdef @ParDataReady
xdef _ParDataReady
xdef _LongCheckSum
xdef @Time10000
xdef _Time10000
_ParAddress: move.l 4(sp),D0 ; address 1-254
@ParAddress:
move.b D0,_ParNetAddr ; store
rts
; ParDataReady()
;
; -1 packet isn't for you
; 0 line is idle
; 1 packet probably pending for you
@ParDataReady:
_ParDataReady: move.l _ParDAT,A0 ; data register
move.l _ParCAT,A1 ; data control register
.pdstable move.b (A1),D0
move.b (A0),D1
cmp.b (A1),D0 ; control lines stable?
bne .pdstable
; Now, ParDataReady might be called after the sending machine
; has aquired but before it can assert REQ. However, the
; sending machine has already (guarenteed) placed its address
; on the data port. So while the address matches, loop while
; REQ not asserted.
btst.l #1,D0 ; ~Req asserted?
beq .pd10 ; beq yes
cmp.b _ParNetAddr,D1 ; no, does data match anyway?
beq .pdstable ; YES, loop until get ~REQ or
bra .pdfail ; data bad.
.pd10 btst.l #2,D0 ; yes, Ctl ?
beq .pdrn ; no, middle of some packet
cmp.b _ParNetAddr,D1 ; yes, my address?
bne .pdrn
moveq.l #1,D0 ; yes, packet (probably) for us
rts
.pdfail btst.l #2,D0 ; fail due to ~Req not asserted
beq .pdrn ; Ctl = 0, line busy
.pdr0 moveq.l #0,D0 ; line idle
rts
.pdrn moveq.l #-1,D0 ; line busy, packet not for me
rts
_ParReadV: ; Read Into Vector
movem.l D2-D7/A2-A5,-(sp)
lea 12+40(sp),A3
bra .rm000
_ParRead:
movem.l D2-D7/A2-A5,-(sp)
lea Null,A3 ; Pointer to next vector
.rm000 move.l 4+40(sp),A0 ; A0 = buffer to read into
move.l 8+40(sp),D7 ; D7 = # bytes to read (maximum)
move.l _ParDAT,A1 ; A1 = data reg
move.l _ParDDR,A2 ; A2 = ddr
move.l _ParCAT,A4 ; A4 = data reg
move.l _ParCDR,A5 ; A5 = ddr
move.b #0,(A2) ; ensure all are inputs
bclr.b #0,(A5)
bclr.b #1,(A5)
bclr.b #2,(A5)
move.l _ParLLTimeout,D5 ; D5 = timeout load
moveq.l #-1,D6 ; D6 = # bytes read so far
; WAIT LOOK FOR ADDRESS MARK
;
; Ctl = 1, ~DReq = 0
move.l D5,D4 ; D4 = timeout countdown
.rmstab move.b (A4),D0 ; control data
move.b (A1),D1 ; data data (network addr)
cmp.b (A4),D0
bne .rmstab
btst.l #2,D0 ; expect CTL = 1
beq .rms1 ; nope
btst.l #1,D0 ; expect ~REQ = 0
beq .rms2 ; yes
.rms1
ifne DEBUG
add.l #1,_ParDebug+0
endc
subq.l #1,D4 ; timeout
bne .rmstab
bra .rmend ; no address mark!
.rms2 cmp.b _ParNetAddr,D1 ; my address?
bne .rms1 ; no, timeout loop
; My address, ~Ack byte.
bclr.b #0,(A4) ; set ~ACK to 0
bset.b #0,(A5) ; set to output
move.l D5,D4 ; reset timeout
.rms4 btst.b #1,(A4) ; wait for ~REQ to go away
bne .rms5
ifne DEBUG
add.l #1,_ParDebug+4
endc
subq.l #1,D4
bne .rms4
moveq.l #-2,D6 ; ~REQ not released ?????
bra .rmend
.rms5 bset.b #0,(A4) ; release ~ACK
moveq.l #0,D6 ; set # bytes read to 0
bra .rms10 ; skip past move
; MAIN READ LOOP
;
; D6 holds cnt, A0 buffer ptr, D0-D4 free to allocate
.rms10loop
move.b D0,(A0) ; store data
addq.l #1,A0 ; next addr.
.rms10 btst.b #1,(A4) ; wait for ~REQ asserted
beq .rms20
btst.b #1,(A4)
beq .rms20
move.l D5,D4 ; load timeout
.rms11 btst.b #1,(A4) ; wait for ~REQ asserted w/to
beq .rms20
ifne DEBUG
add.l #1,_ParDebug+8
endc
subq.l #1,D4
bne .rms11
bra .rmend
.rms20 move.b (A1),D0 ; get data and
bclr.b #0,(A4) ; assert ~ACK
; note, on CTL = 1 end sequence this data item is a dummy
move.b D0,(A0) ; store data
addq.l #1,A0 ; next addr.
addq.l #2,D6 ; optimized but not quite
; true, we've only written 1 sf.
btst.b #1,(A4) ; wait for ~REQ released
bne .rms30
btst.b #1,(A4)
bne .rms30
move.l D5,D4
.rms21 btst.b #1,(A4) ; wait for ~REQ rel w/ to
bne .rms30
ifne DEBUG
add.l #1,_ParDebug+12
endc
subq.l #1,D4
bne .rms21
bra .rmendsub ; sub because D6 is 2 ahead
.rms30 move.b (A1),D0 ; get data
move.b (A4),D1 ; get CTL status
bset.b #0,(A4) ; release ~ACK
btst.l #2,D1 ; EOP if CTL = 1
bne .rmeop
; CANNOT STORE DATA HERE! In case odd # bytes requested,
; second byte would overflow buffer
subq.l #2,D7 ; # bytes remaining
bgt .rms10loop
bne .rmnlb
move.b D0,(A0) ; if D7 = 0 its even and we
; should store the last byte
.rmnlb cmp.l #-1,D7 ; -1 = was odd #
bne .rmeven ; fixup count
subq.l #1,D6
.rmeven
.rmsev0 tst.l (A3) ; if next buffer NULL
beq .rmovflow
move.l (A3)+,A0 ; next buffer
move.l (A3)+,D7
beq .rmsev0 ; 0 bytes, goto next buffer
bra .rms10 ; loop, continue reading
.rmovflow
lea _DummyBuf,A0 ; overflow, dummy buffer
bra .rms10
.rmeop tst.b D0 ; EOP data better be 0!
beq .rmendsub
moveq.l #-3,D6
bra .rmend
.rmendsub subq.l #2,D6 ; because we were two ahead
.rmend bset.b #0,(A4) ; active pull up before (?)
bclr.b #0,(A5) ; setting ~ACK to input
move.l D6,D0 ; return value
movem.l (sp)+,D2-D7/A2-A5 ; restore registers
rts
_ParWriteV: movem.l D2-D7/A2-A6,-(sp) ; write vector
lea 16+44(sp),A3
bra .wm000
_ParWrite:
movem.l D2-D7/A2-A6,-(sp)
lea Null,A3
.wm000 move.l 4+44(sp),D3 ; D3 = destination address
move.l 8+44(sp),A0 ; A0 = buffer to write
move.l 12+44(sp),D7 ; D7 = # bytes to write
move.l _ParDAT,A1 ; A1 = data reg
move.l _ParDDR,A2 ; A2 = ddr
move.l _ParCAT,A4 ; A4 = data reg
move.l _ParCDR,A5 ; A5 = ddr
; move.l 4,A6 ; SYSBase
move.l _SysBase,A6 ; SYSBase
move.b #0,(A2)
and.b #%11111000,(A5)
move.l _ParLLTimeout,D5 ; D5 = timeout load
move.l D5,D4 ; D4 = timeout countdown
moveq.l #-2,D6 ; D6 = # bytes written
; AQUIRE THE LINE USING ~ACK
.wmstab
bset.b #0,(A4) ; so is a 1 when we set it to w
DISABLE
move.b (A4),D0 ; get stable data
move.b (A1),D1
cmp.b (A4),D0
beq .wmstab1
ENABLE
bra .wmstab
; Ints still disabled
; D0 holds ~ACK ~REQ CTL status
.wmstab1 and.b #%111,D0 ; ~ACK=1, ~REQ=1, CTL=1
cmp.b #%111,D0
beq .wm02
; no, if CTL = 1, ~REQ = 0, and D1 = my address then
; return w/ -2
btst.l #1,D0
bne .wm01
btst.l #2,D0
beq .wm01
cmp.b _ParNetAddr,D1 ; somebody calling me?
bne .wm01
ENABLE
bra .wmend
.wm01
ENABLE
ifne DEBUG
add.l #1,_ParDebug+16
endc
subq.l #1,D4
bne .wmstab
bra .wmend
; interrupts still disabled
; we almost own the line
.wm02 bset.b #0,(A5) ; set ~ACK to an output
nop
bclr.b #0,(A4) ; assert ~ACK
bne .wm05 ; was released before, have line!
; don't have line,
bclr.b #0,(A5) ; set back to input
bra .wm01
; Line now aquired.
.wm05
ENABLE
move.b #$FF,(A2) ; set data ddr to outputs
move.b D3,(A1) ; set data lines to our addr
; Before asserting ~REQ pulse CTL to cause interrupt on remote
; machines. Note that our address is already on the data
; lines.
bset.b #2,(A5) ; set CTL to output
bclr.b #2,(A4) ; pulse CTL to cause FLAG int
or.b #%00000111,(A4) ; set CTL = 1 and make sure
; REQ will be one when we
bset.b #1,(A5) ; set ~REQ to output
bclr.b #1,(A4) ; assert ~REQ
bclr.b #0,(A5) ; make ~ACK an input
; (note that REQ->0 before ACK->release)
moveq.l #-1,D6 ; D6 = # bytes written
; INTERRUPTS ENABLED FOR TXFER (fully handshaked)
;
; Address mark ~ACK, wait for ~ACK asserted
.wm10 btst.b #0,(A4)
beq .wm15
move.l D5,D4 ; D4 = timeout countdown
.wm11 btst.b #0,(A4)
beq .wm15
ifne DEBUG
add.l #1,_ParDebug+20
endc
subq.l #1,D4
bne .wm11
bra .wmend
; got ack, now set CTL = 0 (leaves at least one line 0 so
; nobody else thinks the bus is idle!)
;
; note: Since this is the address mark, and is sampled by
; the reader before it asserts ~ACK, I can set CTL
; = 0 now instead of waiting till after ~ACK is
; released.
.wm15 bclr.b #2,(A4) ; set CTL = 0 for duration of pkt
nop ; ???
bset.b #1,(A4) ; release ~REQ
moveq.l #0,D6 ; # bytes written
; DATA XFER LOOP
;
; wait for ~ACK to be released (->1). If no more bytes
; then skip to .wm50
.wm20
tst.l D7 ; more data in this buffer?
ble .wm50 ; nope.
btst.b #0,(A4) ; wait ~ACK released
bne .wm30
move.l D5,D4 ; D4 = timeout countdown
.wm21 btst.b #0,(A4)
bne .wm30 ; need the timeout here?
btst.b #0,(A4)
bne .wm30
ifne DEBUG
add.l #1,_ParDebug+24
endc
subq.l #1,D4
bne .wm21
bra .wmend
; Assert ~REQ for this data byte and wait for ~ACK
.wm30
move.b (A0)+,D0 ; get next data byte
move.b D0,(A1) ; store data and
bclr.b #1,(A4) ; assert ~REQ
move.b (A0)+,D0 ; get next data byte
subq.l #2,D7 ; one less byte (this and next)
addq.l #1,D6 ; # bytes written (this only)
; (not valid until we get ACK
; which is why the wmendsub
btst.b #0,(A4) ; wait for ACK
beq .wm40
btst.b #0,(A4)
beq .wm40
move.l D5,D4 ; D4 = timeout countdown
.wm31 btst.b #0,(A4)
beq .wm40
ifne DEBUG
add.l #1,_ParDebug+28
endc
subq.l #1,D4
bne .wm31
bra .wmendsub
; Have ~ACK, byte transmitted. ++bytes written, --bytes left
; and loop
.wm40 move.b D0,(A1) ; store second byte
bset.b #1,(A4) ; release ~REQ
addq.l #1,D6 ; # bytes written
bra .wm20
; Last byte in buffer has been transmitted.
;
; Get next buffer in vector
.wm50 tst.l (A3)
beq .wm50a
move.l (A3)+,A0 ; buffer ptr
move.l (A3)+,D7 ; # bytes
bra .wm20 ; loop to top
.wm50a
; Last byte has been transmitted,
;
; Wait for ~ACK to be released and then assert ~REQ with
; EOP & CTL = 1
;
; (timing on read is that CTL is sampled when ~REQ is
; RELEASED so no timing window here)
btst.b #0,(A4) ; Wait ~ACK released
beq .wm50
move.b #0,(A1) ; EOP mark (0)
bclr.b #1,(A4) ; assert ~REQ
; Wait for ~ACK asserted
btst.b #0,(A4)
beq .wm60
move.l D5,D4
.wm51 btst.b #0,(A4)
beq .wm60
ifne DEBUG
add.l #1,_ParDebug+32
endc
subq.l #1,D4
bne .wm51
moveq.l #-3,D6 ; EOP failed
bra .wmend
; Set CTL = 1 then release ~REQ, then wait for ~ACK released
.wm60 or.b #%00000100,(A4) ; set CTL = 1
or.b #%00000110,(A4) ; release ~REQ
; Wait ~ACK released ?
.wm61 btst.b #0,(A4)
beq .wm61
; Add D7 to D6. This handles fixup if an odd number of bytes
; were requested written, D7 will be -1 (odd) or 0 (even) and
; D6 will be one too large (odd) or perfect (even)
add.l D7,D6
bra .wmend
.wmendsub subq.l #1,D6 ; was ahead in count
.wmend move.b #0,(A2) ; set data port to input
and.b #%11111000,(A5) ; set data port for ctl lines to input
move.l D6,D0 ; return value
movem.l (sp)+,D2-D7/A2-A6 ; restore registers
rts
; sum = LongCheckSum(buf, bytes)
; (buffer must be lw aligned and bytes must be multiples of 4)
_LongCheckSum:
moveq.l #0,D0 ; D0 = accumulated checksum
move.l 4(sp),A0 ; A0 = ptr
move.l 8(sp),D1 ; D1 = bytes
beq .pcrts
.pc10 add.l (A0)+,D0
subq.l #4,D1
bgt .pc10
tst.l D1
bne .pc20 ; not multiple of 4 bytes!
.pcrts rts ; return checksum
.pc20 illegal ; cause task-held msg
rts
; Delays 10000 rough timeout loops, used to determine
; timeout on init
@Time10000:
_Time10000:
move.l #10000,D4
t10 move.l D4,D4
move.l D4,D4
move.l D4,D4
move.l D4,D4
subq.l #1,D4
bne t10
rts
END